<?php

/**
 * Resource file for France style mailing addresses
 *
 * PHP version 5
 *
 * LICENSE:
 *
 * Copyright (c) 2008-2009, Philippe Jausions / 11abacus
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   - Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   - Neither the name of 11abacus nor the names of its contributors may
 *     be used to endorse or promote products derived from this software
 *     without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @category  Structures
 * @package   Structures_Location
 * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
 * @copyright 2008-2009 by Philippe Jausions / 11abacus
 * @license   http://www.opensource.net/licenses/bsd-license.php New BSD
 */

/**
 * Required dependencies
 */
require_once 'Structures/Location/PostalResourceInterface.php';

/**
 * Resource file for France style mailing addresses
 *
 * @category Structures
 * @package  Structures_Location
 * @author   Philippe Jausions <Philippe.Jausions@11abacus.com>
 */
class Structures_Location_FR_Resource 
implements Structures_Location_PostalResourceInterface
{
    /**
     * France's regions
     * 
     * Most are INSEE-assigned codes, except for "CM" and "NC"
     *
     * @var array (Unofficial code) => name
     */
    static protected $regions = array(
        '42' => 'Alsace',
        '72' => 'Aquitaine',
        '83' => 'Auvergne',
        '25' => 'Basse-Normandie',
        '26' => 'Bourgogne',
        '53' => 'Bretagne',
        '24' => 'Centre',
        '21' => 'Champagne-Ardenne',
        '94' => 'Corse',
        '43' => 'Franche-Comté',
        '23' => 'Haute-Normandie',
        '11' => 'Ile-de-France',
        '91' => 'Languedoc-Roussillon',
        '74' => 'Limousin',
        '41' => 'Lorraine',
        '73' => 'Midi-Pyrénées',
        '31' => 'Nord-Pas-de-Calais',
        '52' => 'Pays de la Loire',
        '22' => 'Picardie',
        '54' => 'Poitou-Charentes',
        '93' => 'Province-Alpes-Côte d\'Azur',
        '82' => 'Rhône-Alpes',
        '02' => 'Martinique',
        '01' => 'Guadeloupe',
        '03' => 'Guyane',
        '04' => 'La Réunion',
        'CM' => 'Collectivités d\'outre-mer',
        'NC' => 'Nouvelle Calédonie',
    );
    
    /**
     * List of departements and their codes
     *
     * @var array Code => name
     */
    static protected $departements = array(
        '42' => array(
            '67' => 'Bas-Rhin',
            '68' => 'Haut-Rhin',
        ),
        '72' => array(
            '24' => 'Dordogne',
            '33' => 'Gironde',
            '40' => 'Landes',
            '47' => 'Lot-et-Garonne',
            '64' => 'Pyrénées-Atlantiques',
        ),
        '83' => array(
            '03' => 'Allier',
            '15' => 'Cantal',
            '43' => 'Haute-Loire',
            '63' => 'Puy-de-Dôme',
        ),
        '25' => array(
            '14' => 'Calvados',
            '50' => 'Manche',
            '61' => 'Orne',
        ),
        '26' => array(
            '21' => 'Côte d\'Or',
            '58' => 'Nièvre',
            '71' => 'Saône-et-Loire',
            '89' => 'Yonne',
        ),
        '53' => array(
            '22' => 'Côtes d\'Armor',
            '29' => 'Finistère',
            '35' => 'Ille-et-Vilaine',
            '56' => 'Morbihan',
        ),
        '24' => array(
            '18' => 'Cher',
            '28' => 'Eure-et-Loir',
            '36' => 'Indre',
            '37' => 'Indre-et-Loire',
            '41' => 'Loir-et-Cher',
            '45' => 'Loiret',
        ),
        '21' => array(
            '08' => 'Ardennes',
            '10' => 'Aube',
            '51' => 'Marne',
            '52' => 'Haute-Marne',
        ),
        '94' => array(
            '2A' => 'Corse du Sud',
            '2B' => 'Haute-Corse',
        ),
        '43' => array(
            '25' => 'Doubs',
            '39' => 'Jura',
            '70' => 'Haute-Saône',
            '90' => 'Territoire de Belfort',
        ),
        '23' => array(
            '27' => 'Eure',
            '76' => 'Seine-Maritime',
        ),
        '11' => array(
            '75' => 'Paris',
            '77' => 'Seine-et-Marne',
            '78' => 'Yvelines',
            '91' => 'Essonne',
            '92' => 'Hauts-de-Seine',
            '93' => 'Seine-St-Denis',
            '94' => 'Val de Marne',
            '95' => 'Val d\'Oise',
        ),
        '91' => array(
            '11' => 'Aude',
            '30' => 'Gard',
            '34' => 'Hérault',
            '48' => 'Lozère',
            '66' => 'Pyrénées Orientales',
        ),
        '74' => array(
            '19' => 'Corrèze',
            '23' => 'Creuse',
            '87' => 'Haute-Vienne',
        ),
        '41' => array(
            '54' => 'Meurthe-et-Moselle',
            '55' => 'Meuse',
            '57' => 'Moselle',
            '88' => 'Vosges',
        ),
        '73' => array(
            '09' => 'Ariège',
            '12' => 'Aveyron',
            '31' => 'Haute-Garonne',
            '32' => 'Gers',
            '46' => 'Lot',
            '65' => 'Hautes-Pyrénées',
            '81' => 'Tarn',
            '82' => 'Tarn-et-Garonne',
        ),
        '31' => array(
            '59' => 'Nord',
            '62' => 'Pas-de-Calais',
        ),
        '52' => array(
            '44' => 'Loire-Atlantique',
            '49' => 'Maine-et-Loire',
            '53' => 'Mayenne',
            '72' => 'Sarthe',
            '85' => 'Vendée',
        ),
        '22' => array(
            '02' => 'Aisne',
            '60' => 'Oise',
            '80' => 'Somme',
        ),
        '54' => array(
            '16' => 'Charente',
            '17' => 'Charente-Maritime',
            '79' => 'Deux-Sèvres',
            '86' => 'Vienne',
        ),
        '93' => array(
            '04' => 'Alpes de Haute-Provence',
            '05' => 'Hautes-Alpes',
            '06' => 'Alpes-Maritimes',
            '13' => 'Bouches-du-Rhône',
            '83' => 'Var',
            '84' => 'Vaucluse',
        ),
        '82' => array(
            '01' => 'Ain',
            '07' => 'Ardèche',
            '26' => 'Drôme',
            '38' => 'Isère',
            '42' => 'Loire',
            '69' => 'Rhône',
            '73' => 'Savoie',
            '74' => 'Haute-Savoie',
        ),
        '02' => array(
            '971' => 'Martinique',
        ),
        '01' => array(
            '972' => 'Guadeloupe',
        ),
        '03' => array(
            '973' => 'Guyane',
        ),
        '04' => array(
            '974' => 'Réunion',
        ),
        'CM' => array(
            '975' => 'Saint-Pierre et Miquelon',
            '976' => 'Mayotte',
            '977' => 'Saint-Martin',
            '978' => 'Saint-Barthélémy',
            '986' => 'Wallis et Futuna',
            '987' => 'Polynésie Française',
        ),
        'NC' => array(
            '988' => 'Nouvelle Calédonie',
        ),
    );

    /**
     * Other "département" codes served by France's postal service
     *
     * @var array Code => name
     */
    static protected $others = array(
        '98' => 'Principauté de Monaco',
        'AD' => 'Principauté d\'Andorre',
    );

    /**
     * List of secondary with their abbreviation and secondary spellings
     *
     * @var array Abbreviation => list of often used equivalent (and typos?)
     */
    static protected $units = array(
        'App.'  => array('Appartement', 'Appmnt.'),
    );

    /**
     * Regions types
     */
    const REGION_TYPE_METROPOLITAIN = 'Metropolitain';
    const REGION_TYPE_DOM = 'DOM';
    const REGION_TYPE_TOM = 'TOM';
    const REGION_TYPE_COM = 'COM';
    const REGION_TYPE_OTHER = 'Other';

    /**
     * List of regions by type
     *
     * @var array Type => region code list
     * @see REGION_TYPE_* constants
     */
    static protected $regionsByType = array(
        self::REGION_TYPE_METROPOLITAIN => array('42', '72', '83', '25',  
            '26', '53', '24', '21', '94', '43', '23', '11', '91', 
            '74', '41', '73', '31', '52', '22', '54', '93', '82'),
        self::REGION_TYPE_DOM => array('01', '02', '03', '04'),
        self::REGION_TYPE_TOM => array(),
        self::REGION_TYPE_COM => array('CM'),
        self::REGION_TYPE_OTHER => array('NC'),
    );

    /**
     * Returns a list of region codes and names
     *
     * @param string $type Region type to narrow the list to
     * 
     * @return array Region code => name
     * @see    getRegionTypes()
     */
    public function getRegions($type = null)
    {
        if ($type === null) {
            return self::$region;

        } elseif (!isset(self::$regionsByType[$type])) {
            throw new Structures_Location_Exception_Invalid('Invalid region type: '.$type);
        }
        return array_intersect_key(self::$regions, 
                                   array_flip($regionsByType[$type]));
    }

    /**
     * Returns a list of region types available
     * 
     * The types returned can be used with getRegions() method
     *
     * @return array Region type => name
     * @see    getRegions()
     */
    public function getRegionTypes()
    {
        $types = array(
            self::REGION_TYPE_METROPOLITAIN => 'France métropolitaine',
            self::REGION_TYPE_DOM => 'Départements d\'outre-mer',
            self::REGION_TYPE_TOM => 'Territoires d\'outre-mer',
            self::REGION_TYPE_COM => 'Communités d\'outre-mer',
            self::REGION_TYPE_OTHER => 'Nouvelle Calédonie',
        );
        return $types;
    }

    /**
     * Returns a list of sub-region types available
     * 
     * The types returned can be used with getSubRegions() method
     *
     * @return array Sub-region type => name
     * @see    getSubRegions()
     */
    public function getSubRegionTypes()
    {
        return $this->getRegionTypes();
    }

    /**
     * Returns list of départements/DOM/TOM/COM/other codes and names
     * 
     * @param string $regionCode NULL or region code
     * @param string $type       Sub-region type to narrow the list to
     *
     * @return array Uppercase 2-char "département" code => name
     * @see    getSubRegionTypes()
     */
    public function getSubRegions($region = null, $type = null)
    {
        if ($region === null) {
            if ($type !== null) {
                $region = $this->getRegions($type);
            } else {
                $region = array_keys(self::$region);
            }
        } elseif ($type !== null) {
            $region = array_intersect((array)$region, $this->getRegions($type));
        }
        return $this->_getDepartements($region);
    }

    /**
     * Returns list of "départements métropolitains" codes and names
     *
     * @return array Uppercase 2-char. code => name
     */
    public function getMetropolitains()
    {
        static $d = null;
        if ($d === null) {
            $d = $this->getSubRegions(null, self::REGION_TYPE_METROPOLITAIN);
        }
        return $d;
    }

    /**
     * Returns a list of départements for the given list of regions
     *
     * @param array $regions Region codes
     * @return array Département code => name
     */
    protected function _getDepartements($regions)
    {
        $d = array();
        settype($regions, 'array');
        foreach ($regions as $region) {
            if (!isset(self::$departements[$region])) {
                throw new Structures_Location_Exception_Invalid('Invalid region code: '. $region);
            }
            $d = array_merge($d, self::$departements[$region]);
        }
        asort($d);
        return $d;
    }

    /**
     * Returns list of France DOM/TOM/COM codes and names, often referred to 
     * as "DOM-TOM".
     *
     * Département et région d'outre-mer (DOM), Territoires d'outre-mer (TOM) and
     * Collectivité d'outre-mer. Classification of these administrative "regions"
     * changed in the past, and most likely will change in the future. 
     * 
     * @return array Uppercase 2-char. DOM/TOM/COM code => name
     */
    public function getDomToms()
    {
        static $d = null;
        if ($d === null) {
            $regions = array_merge($this->getRegions(self::REGION_TYPE_DOM),
                                   $this->getRegions(self::REGION_TYPE_TOM),
                                   $this->getRegions(self::REGION_TYPE_COM),
                                   $this->getRegions(self::REGION_TYPE_OTHER));
            $d = $this->_getDepartements($regions);
        }
        return $d;
    }

    /**
     * Returns a list of secondary units
     *
     * @return array Abbreviation => array[Full word, alternatives...]
     */
    public function getSecondaryUnits()
    {
        return self::$units;
    }

    /**
     * Returns a list of country code for which France's official postal service
     * directly delivers mail for.
     *
     * @return array List of 2 upper case letter country codes (ISO-3166-2)
     */
    public function getPostalDeliveryCountryCodes()
    {
        return array('FR', 'MC', 'AD', 'MQ', 'GP', 'BP', 'GF', 
                     'YT', 'NC', 'RE', 'MF', 'TF', 'FX', 'CF');
    }

    /**
     * Returns the list of countries that is in charge of delivering the mail
     * in France
     *
     * @return array 2 upper case letter country codes (ISO-3166-2)
     */
    public function getPostalDelivererCountryCodes()
    {
        return array('FR');
    }
}

?>